package com.mark.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.mark.basic.contsants.BaseConstants;
import com.mark.basic.dto.EmailDto;
import com.mark.basic.dto.LoginDto;
import com.mark.basic.dto.PhoneDto;
import com.mark.basic.dto.WeChatDto;
import com.mark.basic.jwt.JwtUtils;
import com.mark.basic.jwt.RsaUtils;
import com.mark.basic.jwt.UserInfo;
import com.mark.basic.service.Impl.BaseServiceImpl;
import com.mark.basic.util.*;
import com.mark.org.domain.Wxuser;
import com.mark.org.mapper.WxuserMapper;
import com.mark.system.domain.Menu;
import com.mark.system.mapper.MenuMapper;
import com.mark.system.mapper.PermissionMapper;
import com.mark.user.domain.Logininfo;
import com.mark.user.domain.User;
import com.mark.user.mapper.LogininfoMapper;
import com.mark.user.mapper.UserMapper;
import com.mark.user.service.IUserService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author mark
 * @since 2022-04-01
 */
@Service
public class UserServiceImpl extends BaseServiceImpl<User> implements IUserService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private WxuserMapper wxuserMapper;
    @Autowired
    private MenuMapper menuMapper; // 菜单
    @Autowired
    private PermissionMapper permissionMapper;  // 角色 按钮
    @Value("${jwt.rsa.pri}") // 私钥的文件名
    private String jwtRsaPrivate;

    /**
     *      * //        获取数据 phoneDto
     *      * //        校验参数 用户名、密码、access_token + openid
     *      * //        判断用户是否存在（user）
     *      * //        1、用户存在：查找user
     *      * //        2、用户不存在：生成user + logininfo
     *      * //        保存绑定的数据wxuser表
     *      * //        免密登录，调到主页
     * @param weChatDto
     */
    @Override
    public Map<String, Object> sendregisterWechat(WeChatDto weChatDto) {
        String phone = weChatDto.getPhone();
        String smsCode = weChatDto.getImageCode();
        String access_token = weChatDto.getAccessToken();
        String openid = weChatDto.getOpenId();
        //        校验参数  用户名、密码、access_token + openid
        if (StringUtils.isEmpty(phone)
                || StringUtils.isEmpty(smsCode)
                || StringUtils.isEmpty(access_token)
                || StringUtils.isEmpty(openid)){
            throw  new BusinessException("参数不能为空");
        }
        // 比对验证码
        String Key = BaseConstants.VerifyConstants.BUSINESS_BINDER_PREFIX + phone;
        Object verificationCode = redisTemplate.opsForValue().get(Key);
        //  判断验证码是否为空
        if (verificationCode == null){
            throw new BusinessException("手机验证码不能为空");
        }
        // 判断验证码是否正确
        String smsCodeStr = verificationCode.toString().split(":")[0];
        if(!smsCodeStr.equals(smsCode)){
            throw new BusinessException("手机验证码不正确");
        }
        // 判断用户是否存在（user）
        User user = userMapper.listByPhone(phone);
        if (user == null){
            // 不存在就加一个
            user = wxUser2user(phone);
            Logininfo logininfo1 = user2loginInfo(user);
            logininfoMapper.add(logininfo1);
            // 把新用户存到user表中
            user.setLogininfo_id(logininfo1.getId());
            userMapper.add(user);
        }
        //1 获取微信用户信息
        String url = BaseConstants.WxConstants.GET_USER_URL
                .replace("ACCESS_TOKEN", access_token)
                .replace("OPENID", openid);
        String jsonStr = HttpClientUtils.httpGet(url);
        Wxuser wxuser = JSONObject.parseObject(jsonStr, Wxuser.class);
        System.out.println(wxuser);
        wxuser.setUser_id(user.getId());
        // 保存数据
        wxuserMapper.add(wxuser);
        // 免密登录
        Logininfo logininfo = logininfoMapper.loadByUserId(user.getId());
        Map<String, Object> map = loginSuccessHandler(logininfo);
//            String token = UUID.randomUUID().toString();
//            // 设置有限时间
//            redisTemplate.opsForValue().set(token, logininfo, 30, TimeUnit.MINUTES);
//            // 将token和logininfo装入map，传给接口再到前端
//            Map<String, Object> map = new HashMap<String, Object>();
//            // 安全起见，将盐值和密码置归
//            map.put("token", token);
//            logininfo.setPassword(null);
//            logininfo.setSalt(null);
//            map.put("logininfo", logininfo);
            return map;
    }

    private Map<String, Object> loginSuccessHandler(Logininfo logininfo) {
        UserInfo userInfo = new UserInfo();
        userInfo.setUser(userInfo); // 当前登录人
        // 判断是前端用户还是管理用户
        if (logininfo.getType() == 1){ // 前端用户
            // 前端不用管权限，管理者才需要
            userInfo.setMenus(null);
            userInfo.setMenus(null);
        }else { // 管理者
            // 查询当前登录人的权限
            // 1、菜单权限
            List<Menu> menus = menuMapper.loadMenuByLogininfoId(logininfo.getId());
            // 2、按钮属性
            List<String> permissionSns = permissionMapper.loadPerssionSnByLogininfoId(logininfo.getId());
            // 将有用的权限放入数据中
            userInfo.setMenus(menus);
            userInfo.setPermissions(permissionSns);
        }
        // 将权限信息和登录信息封装成Jwt串
        //获取私钥
        try {
            //获取私钥
            PrivateKey privateKey = RsaUtils
                    .getPrivateKey(FileCopyUtils
                            .copyToByteArray(this.getClass().getClassLoader().getResourceAsStream(jwtRsaPrivate)));
            // 将数据封装成JWT串
            String token = JwtUtils.generateTokenExpireInMinutes(userInfo, privateKey, 30);
            // 将数据传给前台
            Map<String, Object> map = new HashMap<>();
            map.put("token",token);//JWT串  当前登陆人的信息  菜单信息  按钮信息
            //由于我们的前端没有写JWT解密实现，所以我们直接将信息还是原样放回给前端
            logininfo.setSalt(null);
            logininfo.setPassword(null);
            map.put("logininfo",logininfo);
            map.put("menus",userInfo.getMenus());
            map.put("permissions",userInfo.getPermissions());
            return map;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private User wxUser2user(String phone) {
        User user = new User();
        String salt = StrUtils.getComplexRandomString(32);
        String password = MD5Utils.encrypByMd5("6666" + salt);
        user.setPassword(password);
        user.setSalt(salt);
        user.setUsername(phone);
        user.setPhone(phone);
        return user;
    }



//=============================邮箱注册=====================================
    /**
     * 邮箱注册
     *  1、校验邮箱
     *  2、对比密码
     *  3、保存数据到User
     * @param emailDto
     */
    @Override
    public void sendRegisterEmail(EmailDto emailDto) {
        String email = emailDto.getEmail();
        String emailPassword = emailDto.getEmailPassword();
        String emailPasswordRepeat = emailDto.getEmailPasswordRepeat();
        // 邮箱校验
        if (StringUtils.isEmpty(email)){
            throw new BusinessException("请输入邮箱号码");
        }
        // 密码对比
        if (!emailPassword.equals(emailPasswordRepeat)){
            throw  new BusinessException("密码不一致，请重新输入");
        }
        // 保存数据
        // 初始化user
        User user = emailDto2user(emailDto);
        Logininfo logininfo = user2loginInfo(user);
        logininfoMapper.add(logininfo);
        user.setLogininfo_id(logininfo.getId());
        userMapper.add(user);
    }



    private User emailDto2user(EmailDto emailDto) {
        User user = new User();
        String salt = StrUtils.getComplexRandomString(32);
        String password = MD5Utils.encrypByMd5(emailDto.getEmailPassword() + salt);
        user.setPassword(password);
        user.setSalt(salt);
        user.setEmail(emailDto.getEmail());
        return user;
    }


    /**
     *  手机注册
     *  1、手机验证码校验
     *    1.1 手机号码在获取短信验证码的时候都已经校验过是否被注册   --- 当并发量不大的时候这里可以不用再进行校验
     *    1.2 判断验证码是否为空
     *    1.3 判断验证码是否正确
     *  2、密码比对
     *  3、保存到User与logininfo
     */
    @Override
    public void sendRegisterPhone(PhoneDto smsDto) {
        String password = smsDto.getPassword();
        String passwordRepeat = smsDto.getPasswordRepeat();
        String smsCode = smsDto.getSmsCode();
        String phone = smsDto.getPhone();
        // 获取redis中的验证码
        String Key = BaseConstants.VerifyConstants.BUSINESS_REGISTER_PREFIX + phone;
        // 根据key获取redis中的验证码
        Object smsCodeValue = redisTemplate.opsForValue().get(Key);
        //  判断验证码是否为空
        if (smsCodeValue == null){
            throw new BusinessException("手机验证码不能为空");
        }
        // 判断验证码是否正确
        String smsCodeStr = smsCodeValue.toString().split(":")[0];
        if(!smsCodeStr.equals(smsCode)){
            throw new BusinessException("手机验证码不正确");
        }
        // 密码比对
        if (!password.equals(passwordRepeat)){
            throw new BusinessException("密码不一致");
        }
        // 保存到user和loginInfo
        // 初始化user
        User user = dto2user(smsDto);
        // 将user中的值给loginInfo
        Logininfo logininfo = user2loginInfo(user);
        logininfoMapper.add(logininfo);
        // 将loginInfo的id存入user
        user.setLogininfo_id(logininfo.getId());
        userMapper.add(user);
    }

    private Logininfo user2loginInfo(User user) {
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user,logininfo);
        logininfo.setType(1);// 手机号注册
        return logininfo;
    }

    private User dto2user(PhoneDto smsDto) {
        User user = new User();
        user.setPhone(smsDto.getPhone());
        user.setUsername(smsDto.getPhone());
        String salt = StrUtils.getComplexRandomString(32);
        String md5Pwd = MD5Utils.encrypByMd5(smsDto.getPassword() + salt);
        user.setSalt(salt);
        user.setPassword(md5Pwd);
        return user;
    }

    @Override
    public void add(User user) {
        initUser(user);
        Logininfo logininfo = user2loginInfo(user);
        logininfoMapper.add(logininfo);
        user.setLogininfo_id(logininfo.getId());
        userMapper.add(user);
    }

    private void initUser(User user) {
        String salt = StrUtils.getComplexRandomString(32);
        String md5Pwd = MD5Utils.encrypByMd5(user.getPassword() + salt);
        user.setSalt(salt);
        user.setPassword(md5Pwd);
    }
}
